package com.qen.encrypt.http;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * 重新包装HttpServletRequest类
 */
public class DecryptMultipartRequestWrapper extends AbstractMultipartHttpServletRequest implements CustomServletRequest {

    private static final String CONTENT_TYPE = "Content-Type";
    @Nullable
    private Map<String, String[]> multipartParameters;
    @Nullable
    private Map<String, String> multipartParameterContentTypes;

    public DecryptMultipartRequestWrapper(HttpServletRequest request, MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams, Map<String, String> mpParamContentTypes) {
        super(request);
        this.setMultipartFiles(mpFiles);
        this.setMultipartParameters(mpParams);
        this.setMultipartParameterContentTypes(mpParamContentTypes);
    }

    public DecryptMultipartRequestWrapper(HttpServletRequest request, MultipartResolver multipartResolver) {
        super(request);

        MultipartHttpServletRequest multipartRequest = multipartResolver.resolveMultipart(request);
        this.setMultipartParameters(multipartRequest.getParameterMap());
        this.setMultipartFiles(multipartRequest.getMultiFileMap());
        this.setMultipartParameterContentTypes(initializeMultipartParameters(multipartRequest));
    }

    /**
     * 初始化 multipart 参数和内容类型
     */
    private Map<String, String> initializeMultipartParameters(MultipartHttpServletRequest originalRequest) {
        // 解析文件字段及其内容类型
        Map<String, String> multipartParameterContentTypes = new HashMap<>();
        Map<String, MultipartFile> fileMap = originalRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
            String fieldName = entry.getKey();
            MultipartFile file = entry.getValue();
            multipartParameterContentTypes.put(fieldName, file.getContentType());
        }
        // 解析普通表单字段及其内容类型
        Map<String, String[]> parameterMap = originalRequest.getParameterMap();
        for (String paramName : parameterMap.keySet()) {
            // 普通表单字段的内容类型可以设置为 "text/plain" 或其他默认值
            multipartParameterContentTypes.put(paramName, "text/plain");
        }
        return multipartParameterContentTypes;
    }

    @Nullable
    public String getParameter(String name) {
        String[] values = (String[]) this.getMultipartParameters().get(name);
        if (values != null) {
            return values.length > 0 ? values[0] : null;
        } else {
            return super.getParameter(name);
        }
    }

    public String[] getParameterValues(String name) {
        String[] parameterValues = super.getParameterValues(name);
        String[] mpValues = (String[]) this.getMultipartParameters().get(name);
        if (mpValues == null) {
            return parameterValues;
        } else if (parameterValues != null && this.getQueryString() != null) {
            String[] result = new String[mpValues.length + parameterValues.length];
            System.arraycopy(mpValues, 0, result, 0, mpValues.length);
            System.arraycopy(parameterValues, 0, result, mpValues.length, parameterValues.length);
            return result;
        } else {
            return mpValues;
        }
    }

    public Enumeration<String> getParameterNames() {
        Map<String, String[]> multipartParameters = this.getMultipartParameters();
        if (multipartParameters.isEmpty()) {
            return super.getParameterNames();
        } else {
            Set<String> paramNames = new LinkedHashSet();
            paramNames.addAll(Collections.list(super.getParameterNames()));
            paramNames.addAll(multipartParameters.keySet());
            return Collections.enumeration(paramNames);
        }
    }

    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> result = new LinkedHashMap();
        Enumeration<String> names = this.getParameterNames();

        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            result.put(name, this.getParameterValues(name));
        }

        return result;
    }

    public String getMultipartContentType(String paramOrFileName) {
        MultipartFile file = this.getFile(paramOrFileName);
        return file != null ? file.getContentType() : (String) this.getMultipartParameterContentTypes().get(paramOrFileName);
    }

    public HttpHeaders getMultipartHeaders(String paramOrFileName) {
        String contentType = this.getMultipartContentType(paramOrFileName);
        if (contentType != null) {
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Type", contentType);
            return headers;
        } else {
            return null;
        }
    }

    protected final void setMultipartParameters(Map<String, String[]> multipartParameters) {
        this.multipartParameters = multipartParameters;
    }

    protected Map<String, String[]> getMultipartParameters() {
        if (this.multipartParameters == null) {
            this.initializeMultipart();
        }

        return this.multipartParameters;
    }

    protected final void setMultipartParameterContentTypes(Map<String, String> multipartParameterContentTypes) {
        this.multipartParameterContentTypes = multipartParameterContentTypes;
    }

    protected Map<String, String> getMultipartParameterContentTypes() {
        if (this.multipartParameterContentTypes == null) {
            this.initializeMultipart();
        }

        return this.multipartParameterContentTypes;
    }

    @Override
    public String getBodyString() {
        return "";
    }

    @Override
    public void addParameter(String key, String[] value) {
        this.multipartParameters.put(key, value);
    }

}
